/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
	Simple table parsing.. Useless..
*/

#include <shared.h>

#define RSDPTR_SIGN "RSD PTR "

struct node_fs acpi_fs = {
	.type = TYPE_FS,
	.name = "acpi",
	NODE_FS_OPS_INIT,
	NODE_FS_INIT_INIT,
	NODE_FS_LIST_INIT,
	.class = NULL,
};

struct rsdptr {
	u8 sign[8];
	u8 checksum;
	u8 oemid[6];
	u8 res0;
	u32 rsdtaddress;
}__attribute__((packed))rsdptr;

struct header {
	u8 sign[4];
	u32 len;
	u8 rev;
	u8 checksum;
	u8 oemid[6];
	u8 oemtableid[8];
	u32 oemrev;
	u32 creatorid;
	u32 creatorrev;
}__attribute__((packed));

u32 nr_entry = 0;
struct rsdt {
	struct header header;
	u32 entry[];
}__attribute__((packed))rsdt;


u32 nr_apic_structure = 0;
struct apic_dt {
	struct header header;
	u32 local_apic_address;
	u32 flags;
	u8 apic_structure[];
}__attribute__((packed))apic_dt;

struct facp_dt {
	struct header header;
	u32 firmware_ctrl;
	u32 dsdt;
	u8 int_model;
	u8 res0;
	u16 sci_int;
	u32 sci_cmd;
	u8 acpi_enable;
	u8 acpi_disable;
	u8 s4bios_req;
	u8 res1;
	u32 pm1a_evt_blk;
	u32 pm1b_evt_blk;
	u32 pm1a_cnt_blk;
	u32 pm1b_cnt_blk;
	u32 pm2_cnt_blk;
	u32 pm_tmr_blk;
	u32 gpe0_blk;
	u32 gpe1_blk;
	u8 pm1_evt_len;
	u8 pm1_cnt_len;
	u8 pm2_cnt_len;
	u8 pm_tm_len;
	u8 gpe0_blk_len;
	u8 gpe1_blk_len;
	u8 gpe1_base;
	u8 res2;
	u16 p_lvl2_lat;
	u16 p_lvl3_lat;
	u16 flush_size;
	u16 flush_stride;
	u8 duty_offset;
	u8 duty_width;
	u8 day_alrm;
	u8 mon_alrm;
	u8 century;
	u8 res3[3];
	u32 flags;
}__attribute__((packed))facp_dt;

struct facs {
	u8 sign[4];
	u32 len;
	u32 hardware_sign;
	u32 firmware_waking_vector;
	u32 global_lock;
	u32 flags;
	u8 res0[40];
}__attribute__((packed))facs;

int register_table(void *table_addr, u32 var_count)
{
	struct node_fs *n;
	char fs_name[FS_NAME_LEN];
	memcpy(fs_name, table_addr, 4);
	fs_name[4] = 0;
	n = fs_create(fs_name);
	if(!n)
		return EMEMERR;
	n->class = table_addr;
	n->general_counter = var_count;
	return fs_link("/acpi", n);
}

int find_rsdt(void *start, void *end)
{
	void *ptr;
	for(ptr = start; ptr < end; ptr+=16) {
		if(!memcmp(ptr, RSDPTR_SIGN, 8)) {
			memcpy(&rsdptr, ptr, sizeof(struct rsdptr));
			return 0;
		}
	}
	return ENODATA;
}

int init_acpi()
{
	int i, err;
	struct header *table;
	void *ebda;
	ebda = (void *)((*((u16 *)0x40e)) << 4);
	err = find_rsdt(ebda, ebda + 1024);
	if(err) {
		err = find_rsdt((void *)0xe0000, (void *)0xfffff);
		if(err)
			return err;
	}
	fs_link("/", &acpi_fs);
	//First get only the table without the entries and then copy the table taking the 'len' value
	memcpy(&rsdt, (void *)rsdptr.rsdtaddress, sizeof(struct rsdt));
	memcpy(&rsdt, (void *)rsdptr.rsdtaddress, rsdt.header.len);
	register_table(&rsdt, nr_entry);
	//Now compute the number of entries
	nr_entry = (rsdt.header.len - sizeof(struct rsdt)) / 4;
	for(i=0; i<nr_entry; i++) {
		table = (struct header *)rsdt.entry[i];
		if(!memcmp(table->sign, "FACP", 4)) {
			memcpy(&facp_dt, (void *)table, sizeof(struct facp_dt));
			register_table(&facp_dt, 0);
			memcpy(&facs, (void *)facp_dt.firmware_ctrl, sizeof(struct facs));
			register_table(&facs, 0);
		} else if(!memcmp(table->sign, "APIC", 4)) {
			memcpy(&apic_dt, (void *)table, table->len);
			nr_apic_structure = table->len - sizeof(struct apic_dt);
			register_table(&apic_dt, nr_apic_structure);
		}
	}
	return 0;
}
